package JaggSoft.Utilidades;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import net.sourceforge.jdatepicker.impl.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;

public class Util {
    public enum Tipo{ADMINISTRADOR,USUARIO,NINGUNO};
    public enum Estado{OK,CANCELADO,ERROR};
    /**
     * Coloca un selector de fecha en un JPanel y de ser proporcionado tambien otros componentes como JCheckbox, JButton, etc.
     *
     * @param panel panel al cual se colocará el selector de fecha y los componentes suministrados.
     * @param cal (Opcional) calendario por si se requiere utilizar el tipo de calendario personalizado como el de Grecia entre otros, de no necesitarse proporcionar null.
     * @param componentes (Opcional) arreglo de componentes adicionales que se pueden agregar al panel, como: JCheckBox, JButton, JTextField, etc. entre otros, de no necesitarse proporcionar null.
     * @return JDatePickerImpl el cual es un selector de fechas.
     */
    public static JDatePickerImpl ColocarSelectorFecha(JPanel panel,Calendar cal,Component componentes[]){
        JDatePickerImpl selector_fecha;
        panel.setLayout(new FlowLayout());
        for(Component c:componentes){ panel.add(c); }
        UtilDateModel modelo=new UtilDateModel();
        modelo.setSelected(true);
        selector_fecha = new JDatePickerImpl(new JDatePanelImpl(modelo), new DateLabelFormatter());
        panel.add(selector_fecha);
        return selector_fecha;
    }
    /**
     *Indica si un texto es númerico.
     * 
     * @param texto el texto fuente que se verificará si es númerico.
     * @param esDecimal indica si el número es decimal.
     * @return true si es numérico, false de lo contrario.
     */
    public static boolean esNumero(String texto,boolean esDecimal){
        String fuente="0123456789";
        if(!esDecimal){
            for(char c:texto.toCharArray())
                if(!fuente.contains(""+c))
                    return false;
        }
        else{
            int puntos=0;
            for(char c:texto.toCharArray()){
                if(!fuente.contains(""+c)){
                    if(c=='.')
                        puntos++;
                    else
                        return false;
                }
            }
            return (puntos==0||puntos==1);
        }
        return true;
    }
    /**
     *Oculta la columna que se le proporcione en un JTable.
     * 
     * @param tabla la tabla en la cual se ocultara la columna.
     * @param col el número de columna que se ocultara.
     */
    public static void OcultarColumna(JTable tabla,int col){
        tabla.getColumnModel().getColumn(col).setMaxWidth(0);
        tabla.getColumnModel().getColumn(col).setMinWidth(0);
        tabla.getTableHeader().getColumnModel().getColumn(col).setMaxWidth(0);
        tabla.getTableHeader().getColumnModel().getColumn(col).setMinWidth(0);
    }
    /**
     * Carga los datos en un JTable según la consulta que se le proporcione.
     *
     * @param tabla la tabla que se cargaran los datos.
     * @param consulta la consulta con la cuál se cargara la tabla.
     * @param columnas los títulos de las columnas de la tabla, deben tener la misma cantidad de columnas que las de la consulta.
     */
    public static void CargarTabla(JTable tabla,String consulta,String columnas[]){
        Object[] datos=new Object[columnas.length];
        MyModeloTabla modelo=new MyModeloTabla(columnas);
        JTable tmp_tabla=Conexion.ObtenerTabla(consulta,columnas);
        for(int i=0;i<tmp_tabla.getRowCount();i++){
            for(int j=0;j<tmp_tabla.getColumnCount();j++)
                datos[j]=tmp_tabla.getValueAt(i,j);
            modelo.AgregarFila(datos);
        }
        try{
            tabla.setModel(modelo);
        }catch(Exception e){System.err.println(e.getMessage());}
        datos=null;
        tmp_tabla=null;
    }
    /**
     *Carga los datos en un JTable según la consulta que se le proporcione y lo establece de forma que se puedan editar los campos que se le indiquen.
     * 
     * @param tabla la tabla que contendra los campos editables.
     * @param consulta la consulta de la tabla.
     * @param columnas los títulos de las columnas de la tabla, deben tener la misma cantidad de columnas que las de la consulta.
     * @param cols_edit arreglo booleano que debe tener el mismo tamaño que la cantidad de columnas, el valor de cada elemento del arreglo booleano indica si es editable o no.
     */
    public static void CargarTablaEditable(JTable tabla,String consulta,String columnas[],boolean[] cols_edit){
        Object[] datos=new Object[columnas.length];
        MyModeloTablaEditable modelo=new MyModeloTablaEditable(columnas,cols_edit);
        JTable tmp_tabla=Conexion.ObtenerTabla(consulta,columnas);
        for(int i=0;i<tmp_tabla.getRowCount();i++){
            for(int j=0;j<tmp_tabla.getColumnCount();j++)
                datos[j]=tmp_tabla.getValueAt(i,j);
            modelo.AgregarFila(datos);
        }
        tabla.setModel(modelo);
        datos=null;
        tmp_tabla=null;
    }
    /**
     *Carga un JComboBox según una consulta proporcionada, la consulta debe contener únicamente dos parametros en cual el 1ro obligatoriamente debe ser el id y el 2do la descripción.
     * 
     * @param combo el JComboBox el cuál sera cargado con los datos.
     * @param consulta la consulta que debe tener id y descripción únicamente como campos.
     * @param columnas las columnas con el título de la tabla de la consulta.
     */
    public static void CargarJComboBox(JComboBox<ModeloItemValor> combo,String consulta,String[] columnas){ //Consulta siempre 1ero el ID, 2do la Descripcion
        combo.removeAllItems();
        JTable tabla=Conexion.ObtenerTabla(consulta,columnas);
        for(int i=0;i<tabla.getRowCount();i++)
            combo.addItem(new ModeloItemValor(tabla.getValueAt(i,1),tabla.getValueAt(i,0)));
        tabla=null;
        columnas=null;
    }
    /**
     *Establece el Editor de Celda de un JTable, útil a la hora de obligar a que solo se ingresen números en un campo del JTable. Esto se realiza por tipo de clase de un campo, por ejemplo se puede aplicar para todos los campos Integer, o todos los campos String, etc.
     * 
     * @param tabla la tabla a la cual se le colocara el editor de celda.
     * @param valor_inicial valor por default que tendra la celda.
     * @param clase clase de la celda a las cuales surtira el efecto, ejemplos: Integer.class, String.class, etc.
     */
    public static void ColocarEditorCelda(JTable tabla,String valor_inicial,Class clase){
        JTextField jtf=new JTextField(valor_inicial);
        jtf.setBorder(BorderFactory.createEmptyBorder());
        tabla.setDefaultEditor(clase,new EditorCelda(jtf));
    }
    /**
     *Alinea todas las celdas de un JTable según la alineacion que se proporcione.
     * 
     * @param tabla la tabla a ser alineada.
     * @param alineacion el tipo de alineación, ya sea SwingConstants.LEFT, SwingConstants.RIGHT o SwingConstants.CENTER.
     */
    public static void AlinearColumnas(JTable tabla,int alineacion){
        DefaultTableCellRenderer render=new DefaultTableCellRenderer();
        render.setHorizontalAlignment(alineacion);
        ((DefaultTableCellRenderer)tabla.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(alineacion);
        for(int i=0;i<tabla.getColumnCount();i++)
            tabla.getColumnModel().getColumn(i).setCellRenderer(render);
    }
    /**
     *Alinea la celda proporcionada de un JTable según la alineacion que tambien se proporcione.
     * @param tabla  la tabla a ser alineada.
     * @param alineacion el tipo de alineación, ya sea SwingConstants.LEFT, SwingConstants.RIGHT o SwingConstants.CENTER.
     * @param columna la columna a ser alineada.
     */
    public static void AlinearColumna(JTable tabla,int alineacion,int columna){
        DefaultTableCellRenderer render=new DefaultTableCellRenderer();
        render.setHorizontalAlignment(alineacion);
        tabla.getColumnModel().getColumn(columna).setCellRenderer(render);
    }
    /**
     *Habilita la eliminación de filas en un JTable al seleccionar una o mas filas y presionar la tecla DELETE.
     * 
     * @param tabla la tabla a la cual se habilitara la eliminación de filas al presionar la tecla DELETE.
     */
    public static void EstablecerEliminadorFila(JTable tabla){
        InputMap im=tabla.getInputMap(JTable.WHEN_FOCUSED);
        ActionMap am=tabla.getActionMap();
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0),"delete");
        am.put("delete",new AccionAbstractaTabla(tabla));
    }
    /**
     *Establece una columna a tipo ComboBox.
     * 
     * @param tabla la tabla que tendra la columna tipo ComboBox.
     * @param columna la columna a la que se le agregara el objeto ComboBox.
     * @param datos arreglo de datos, que tendra el ComboBox.
     */
    public static void EstablecerColumnaCombobox(JTable tabla,int columna,Object[] datos){
        TableColumn col=tabla.getColumnModel().getColumn(columna);
        col.setCellRenderer(new MyRendidorComboBox(datos));
        col.setCellEditor(new MyEditorComboBox(datos));
    }
    /**
     *Verifica si el texto del manejador de evento de tecla presionada es un caracter alfanumérico
     * 
     * @param evt el manjador de evento de tecla presionada.
     * @return true si es Alfanúmerico o tiene carácteres válidos, de lo contrario falso.
     */
    public static boolean SoloAlfanumericos(KeyEvent evt){
        char tecla=evt.getKeyChar();
        return !((tecla<'a'||tecla>'z')&&(tecla<'A'||tecla>'Z')&&(tecla<'0'||tecla>'9')&&(tecla!=8)&&(tecla!=35)&&(tecla!=36)&&(tecla!=127)
                &&(tecla!='ñ')&&(tecla!='Ñ')&&(tecla!=' ')&&(tecla!=39));
    }
    public static String ComponerTexto(String texto){
        int i=0;
        if(texto.equals("")) return texto;
        while(i<texto.length()-1){
            if(texto.charAt(i)==(char)39){
                texto=texto.substring(0,i)+"\\"+texto.substring(i);
                i+=2;
            }
            else
                i++;
        }
        i=texto.length()-1;
        if(texto.charAt(i)==(char)39)
            texto=texto.substring(0,i)+"\\"+texto.substring(i);
        return texto;
    }
    public static String IniciarBitacora(String titulo){
        SimpleDateFormat formato=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Conexion.GuardarBitacora("INSERT INTO bitacora(id_usuario,fecha_acceso,formulario) VALUES("+Conexion.ID+",'"+formato.format(new Date())+"','"+titulo+"');");
        JTable tabla=Conexion.ObtenerTabla("SELECT MAX(id_bitacora) FROM bitacora",new String[]{"bitacora"});
        return tabla.getValueAt(0,0).toString();
    }
    public static void ClausurarBitacora(String id){
        SimpleDateFormat formato=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Conexion.GuardarBitacora("UPDATE bitacora SET fecha_clausura='"+formato.format(new Date())+"' WHERE id_bitacora="+id);
    }
    public static void AccionBitacora(String bitacora,String accion){
        SimpleDateFormat formato=new SimpleDateFormat("HH:mm:ss");
        Conexion.GuardarBitacora("INSERT INTO detallebitacora(id_bitacora,accion,hora) VALUES("+bitacora+",'"+accion+"','"+formato.format(new Date())+"')");
    }
    public static Estado ExportarExcel(JTable tabla,File archivo_excel,String nombre_hoja){
        try {
            if(archivo_excel.exists()){
                if(JOptionPane.showConfirmDialog(null,"¿Desea sustituir el archivo?","El archivo ya existe",JOptionPane.YES_NO_OPTION)==JOptionPane.OK_OPTION)
                    archivo_excel.createNewFile();
                else
                    return Estado.CANCELADO;
            }
            else
                archivo_excel.createNewFile();
            
            Workbook libro=new HSSFWorkbook();
            Sheet hoja=libro.createSheet(nombre_hoja);
            HSSFCellStyle estilo[]=getEstilos((HSSFWorkbook)libro);
            AgregarFilaTitulo(tabla,0,hoja.createRow(0),estilo[0]);
            
            for(int i=0;i<tabla.getRowCount();i++){
                if(i!=tabla.getRowCount()-1)
                    AgregarFila(tabla,i,0,hoja.createRow(i+1),estilo[1]);
                else
                    AgregarFila(tabla,i,0,hoja.createRow(i+1),estilo[2]);
            }
            try (FileOutputStream archivo = new FileOutputStream(archivo_excel)) {
                libro.write(archivo);
                return Estado.OK;
            }
        } catch(Exception ex){System.err.println(ex.getMessage());}
        return Estado.ERROR;
    }
    public static Estado ExportarReporteExcel(JTable tabla,File archivo_excel,String nombre_hoja,String subconsulta,String[] columnas){
        try{
            if(archivo_excel.exists()){
                if(JOptionPane.showConfirmDialog(null,"¿Desea sustituir el archivo?","El archivo ya existe",JOptionPane.YES_NO_OPTION)==JOptionPane.OK_OPTION)
                    archivo_excel.createNewFile();
                else
                    return Estado.CANCELADO;
            }
            else
                archivo_excel.createNewFile();
            
            Workbook libro=new HSSFWorkbook();
            Sheet hoja=libro.createSheet(nombre_hoja);
            HSSFCellStyle estilo[]=getEstilos((HSSFWorkbook)libro);
            int col=0,fil=0;
            
            for(int f=0;f<tabla.getRowCount();f++){
                AgregarFilaTitulo(tabla,0,hoja.createRow(fil++),estilo[0]);
                AgregarFila(tabla,f,0,hoja.createRow(fil),estilo[2]);
                fil+=2;
                
                JTable tabla_detalle=Conexion.ObtenerTabla(subconsulta.replaceAll("@id",tabla.getValueAt(f,0).toString()),columnas);
                AgregarFilaTitulo(tabla_detalle,1,hoja.createRow(fil++),estilo[0]);
                for(int i=0;i<tabla_detalle.getRowCount();i++){
                    if(i!=tabla_detalle.getRowCount()-1)
                        AgregarFila(tabla_detalle,i,1,hoja.createRow(fil++),estilo[1]);
                    else
                        AgregarFila(tabla_detalle,i,1,hoja.createRow(fil),estilo[2]);
                }
                fil+=2;
            }
            try (FileOutputStream archivo = new FileOutputStream(archivo_excel)) {
                libro.write(archivo);
                return Estado.OK;
            }
        } catch(Exception ex){ex.printStackTrace(); System.err.println(ex.getMessage());}
        return Estado.ERROR;
    }
    private static HSSFCellStyle[] getEstilos(HSSFWorkbook libro){
        HSSFFont fuente=((HSSFWorkbook)libro).createFont();
        fuente.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        HSSFCellStyle estilo[]=new HSSFCellStyle[3];

        estilo[0]=((HSSFWorkbook)libro).createCellStyle();
        estilo[0].setFont(fuente);
        estilo[0].setBorderBottom(HSSFCellStyle.BORDER_THIN);
        estilo[0].setBorderLeft(HSSFCellStyle.BORDER_THIN);
        estilo[0].setBorderRight(HSSFCellStyle.BORDER_THIN);
        estilo[0].setBorderTop(HSSFCellStyle.BORDER_THIN);

        estilo[1]=((HSSFWorkbook)libro).createCellStyle();
        estilo[1].setBorderLeft(HSSFCellStyle.BORDER_THIN);
        estilo[1].setBorderRight(HSSFCellStyle.BORDER_THIN);

        estilo[2]=((HSSFWorkbook)libro).createCellStyle();
        estilo[2].setBorderLeft(HSSFCellStyle.BORDER_THIN);
        estilo[2].setBorderRight(HSSFCellStyle.BORDER_THIN);
        estilo[2].setBorderBottom(HSSFCellStyle.BORDER_THIN);

        return estilo;
    }
    private static void AgregarFila(JTable tabla,int fil,int col,Row fila,HSSFCellStyle estilo){
        for(int c=0;c<tabla.getColumnCount();c++){
            if(tabla.getColumnModel().getColumn(c).getMaxWidth()!=0){
                Cell celda=fila.createCell(col++);
                String clase=tabla.getColumnClass(c).getName();
                celda.setCellStyle(estilo);
                if(clase.substring(clase.lastIndexOf(".")+1).equals("Integer")||clase.substring(clase.lastIndexOf(".")+1).equals("Long"))
                    celda.setCellValue(new Long(tabla.getValueAt(fil,c).toString()));
                else{
                    if(clase.substring(clase.lastIndexOf(".")+1).equals("String"))
                        celda.setCellValue(tabla.getValueAt(fil,c).toString());
                    else{
                        if(clase.substring(clase.lastIndexOf(".")+1).equals("Double")||clase.substring(clase.lastIndexOf(".")+1).equals("BigDecimal"))
                            celda.setCellValue(new Double(tabla.getValueAt(fil,c).toString()));
                        else
                            System.err.println(clase.substring(clase.lastIndexOf(".")+1)+",pos:"+col);
                    }
                }
            }
        }
    }
    private static void AgregarFilaTitulo(JTable tabla,int col,Row fila,HSSFCellStyle estilo){
        for(int c=0;c<tabla.getColumnCount();c++){
            if(tabla.getColumnModel().getColumn(c).getMaxWidth()!=0){
                Cell celda=fila.createCell(col++);
                celda.setCellStyle(estilo);
                celda.setCellValue(tabla.getColumnName(c));
            }
        }
    }
}